package com.code.soulnetty.modbus.server.handler;

import com.code.soulnetty.modbus.biz.ModbusCache;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;

import java.net.InetSocketAddress;

/**
 * 模拟modbus服务
 *
 * @author lyf
 * @date 2022-12-16 21:40
 */
@Slf4j
public class ModbusTcpHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private ModbusCache modbusCache;

    public ModbusTcpHandler(ModbusCache modbusCache) {
        this.modbusCache = modbusCache;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        try {
            read(ctx, msg);
        } catch (Exception e) {
            log.error("read error", e);
        }
    }

    /**
     * 根据channel local port 获取设备信息
     *
     * @param ctx
     * @param msg
     */
    private void read(ChannelHandlerContext ctx, ByteBuf msg) {
        long start = System.currentTimeMillis();
        final InetSocketAddress localAddress = (InetSocketAddress) ctx.channel().localAddress();
        log.info("[READ] data is {},{},{}", ctx.channel().remoteAddress(), localAddress,
                ByteBufUtil.hexDump(msg));
        int curIndex = msg.readerIndex();
        ByteBuf result = ctx.alloc().directBuffer();
        short msgId = msg.readShort();
        short proType = msg.readShort();
        result.writeShort(msgId);
        result.writeShort(proType);
        int len = msg.readUnsignedShort();
        byte unitId = msg.readByte();
        byte fc = msg.readByte();

        int startOffset = msg.readUnsignedShort();
        int count = msg.readUnsignedShort();
        byte[] data = null;
        if (fc == (byte) 1 || fc == (byte) 2) {
            data = modbusCache.getBit(localAddress.getPort(), startOffset, count);
            if (data == null) {
                int totalCount = count >> 3;
                if (count % 8 == 0) {
                    data = RandomUtils.nextBytes(totalCount % 2 == 0 ? totalCount : totalCount + 1);
                } else {
                    //向后多加一个寄存器
                    data = RandomUtils.nextBytes(totalCount % 2 == 0 ? totalCount + 2 : totalCount + 1);
                }

            }
        } else if (fc == (byte) 3 || fc == (byte) 4) {
            int total = count << 1;
            data = modbusCache.get(localAddress.getPort(), startOffset, total);
            if (data == null) {
                data = RandomUtils.nextBytes(total);
            }
        }
        String key = localAddress.getHostName() + ":" + localAddress.getPort() + ":" + msgId;
        int i = RandomUtils.nextInt(1, 10);
        // try {
        //     Thread.sleep(RandomUtils.nextInt(1, 200));
        // } catch (InterruptedException e) {
        //     e.printStackTrace();
        // }
        // log.info("[SLEEP] [{}] [{}]", key, i);
        //没有获取到数据的异常情况
        if (data == null) {
            result.writeShort(3);
            result.writeByte(unitId);
            result.writeByte(fc | 0x80);
            result.writeByte(0x01);
        } else {
            result.writeShort(data.length + 3);
            result.writeByte(unitId);
            result.writeByte(fc);
            result.writeByte(data.length);
            result.writeBytes(data);
        }
        log.info("[RE] [{}] [{}]", key, ByteBufUtil.hexDump(result));
        ctx.writeAndFlush(result).addListener(d -> {
            if (d.isDone()) {
                log.info("[SEN_DONE] cost {},{},{}", localAddress, msgId, (System.currentTimeMillis() - start));
            }
        });
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
        log.info("[CON] channelRegistered {} ==> {}", ctx.channel().remoteAddress(), ctx.channel().localAddress());
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
        log.info("[CON] channelUnregistered {} ==> {}", ctx.channel().remoteAddress(), ctx.channel().localAddress());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        log.info("[CON] channelActive {} ==> {}", ctx.channel().remoteAddress(), ctx.channel().localAddress());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        log.info("[CON] channelInactive {} ==> {}", ctx.channel().remoteAddress(), ctx.channel().localAddress());
    }
}
